//Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Eas.WebCrawler.Interfaces;

namespace Eas.WebCrawler.Test.Scenario {
    /// <summary>
    /// Summary description for AdditionalErrorTests
    /// </summary>
    [TestClass]
    public class AdditionalErrorTests {
        public AdditionalErrorTests() {
            //
            // TODO: Add constructor logic here
            //
        }

        static Dictionary<Uri, object> _ExpectedUris = null;

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        [TestInitialize()]
        public void MyTestInitialize() {
            _ExpectedUris = new Dictionary<Uri, object>();
            LogErrorAction.errors.Clear();
            LogWebFailureAction.failures.Clear();
            LogTraceAction.traces.Clear();
            TestGateway.Clear();
        }

        //
        // Use TestCleanup to run code after each test has run
        [TestCleanup()]
        public void MyTestCleanup() {
            Assert.AreEqual<int>(0, _ExpectedUris.Count);
            _ExpectedUris = null;
            GC.Collect();
            System.Threading.Thread.Sleep(10);
            GC.Collect();
            System.Threading.Thread.Sleep(10);
            GC.Collect();
            System.Threading.Thread.Sleep(10);
            GC.Collect();
        }

        //
        #endregion


        [TestMethod]
        /// <summary>Executes the SuperSimpleTest test.</summary>
        public void SuperSimpleTest() {
            int errorCount = 0;
            int webFailureCount = 0;

            PerformTest(false, errorCount, webFailureCount);
        }

        [TestMethod]
        /// <summary>Executes the TimeoutFromGetResponseTest test.</summary>
        public void TimeoutFromGetResponseTest() {
            int errorCount = 0;
            int webFailureCount = 1;
            PerformTest(false, 0, 30000, 1000, false, errorCount, webFailureCount);
        }

        [TestMethod]
        /// <summary>Executes the TimeoutFromRead test.</summary>
        public void TimeoutFromRead() {
            int errorCount = 0;
            int webFailureCount = 1;
            PerformTest(false, 30000, 0, 1000, false, errorCount, webFailureCount);
        }

        [TestMethod]
        /// <summary>Executes the SuperSimplePauseTest test.</summary>
        public void SuperSimplePauseTest() {
            int errorCount = 0;
            int webFailureCount = 0;

            PerformTest(true, errorCount, webFailureCount);
        }

        [TestMethod]
        /// <summary>Executes the TestNoAfterStopHandler test.</summary>
        public void TestNoAfterStopHandler() {
            int errorCount = 1;
            int webFailureCount = 0;

            int workerCount = 1;
            string[] startingUris = new string[] { "nolinks.html" };
            string[] historyFiles = new string[] { };
            string[] processedUris = new string[] { };
            processedUris = new string[] { "nolinks.html" };

            using (NMock2.Mockery mock = new NMock2.Mockery()) {
                TestEventHandler handler = mock.NewMock<TestEventHandler>();
                using (Eas.WebCrawler.Common.Test.TestDirectory directory = new Eas.WebCrawler.Common.Test.TestDirectory("SimpleScenarioTests", "Resources")) {
                    LoadHistoryFiles(historyFiles, directory);
                    string[] initialUriList = CreateUriList(startingUris, directory);
                    using (Eas.WebCrawler.Interfaces.WebCrawlerEngine engine = TestHelper.CreateEngine(new TestGateway(), handler, directory, workerCount, -1, initialUriList)) {

                        Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent waitEvent = new Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent();
                        engine.Events.Stopped += waitEvent.CreateEventHandler();
                        engine.Events.TraceLevel = TraceLevel.Detailed;
                        LogErrorAction.errors.Clear();
                        PauseAndUnPauseAction pauseAction = new PauseAndUnPauseAction(engine, false);
                        NMock2.Expect.AtLeast(0).On(handler).Method("Error").WithAnyArguments().Will(new LogErrorAction());
                        NMock2.Expect.AtLeast(0).On(handler).Method("Trace").WithAnyArguments().Will(new LogTraceAction(), pauseAction);
                        NMock2.Expect.AtLeast(0).On(handler).Method("UrlFailed").WithAnyArguments().Will(new LogWebFailureAction());
                        NMock2.Expect.AtLeast(0).On(handler).Method("UrlProcessingBegan").WithAnyArguments();
                        NMock2.Expect.Once.On(handler).Method("BeforeStart").WithAnyArguments();

                        ExpectProcessedUris(processedUris, handler, directory, engine);
                        RunEngine(engine, waitEvent);
                        CheckErrors(errorCount, webFailureCount);
                    }
                }
            }

        }

        [TestMethod]
        /// <summary>Executes the TestNoAfterPauseHandler test.</summary>
        public void TestNoAfterPauseHandler() {
            int errorCount = 1;
            int webFailureCount = 0;

            int workerCount = 1;
            string[] startingUris = new string[] { "nolinks.html" };
            string[] historyFiles = new string[] { };
            string[] processedUris = new string[] { };
            processedUris = new string[] { "nolinks.html" };

            using (NMock2.Mockery mock = new NMock2.Mockery()) {
                TestEventHandler handler = mock.NewMock<TestEventHandler>();
                using (Eas.WebCrawler.Common.Test.TestDirectory directory = new Eas.WebCrawler.Common.Test.TestDirectory("SimpleScenarioTests", "Resources")) {
                    LoadHistoryFiles(historyFiles, directory);
                    string[] initialUriList = CreateUriList(startingUris, directory);
                    using (Eas.WebCrawler.Interfaces.WebCrawlerEngine engine = TestHelper.CreateEngine(new TestGateway(), handler, directory, workerCount, -1, initialUriList)) {

                        Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent waitEvent = new Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent();
                        engine.Events.Stopped += waitEvent.CreateEventHandler();
                        engine.Events.TraceLevel = TraceLevel.Detailed;
                        LogErrorAction.errors.Clear();
                        PauseAndUnPauseAction pauseAction = new PauseAndUnPauseAction(engine, true);
                        NMock2.Expect.AtLeast(0).On(handler).Method("Error").WithAnyArguments().Will(new LogErrorAction());
                        NMock2.Expect.AtLeast(0).On(handler).Method("Trace").WithAnyArguments().Will(new LogTraceAction(), pauseAction);
                        NMock2.Expect.AtLeast(0).On(handler).Method("UrlFailed").WithAnyArguments().Will(new LogWebFailureAction());
                        NMock2.Expect.AtLeast(0).On(handler).Method("UrlProcessingBegan").WithAnyArguments();
                        NMock2.Expect.Once.On(handler).Method("BeforeStart").WithAnyArguments();
                        NMock2.Expect.Once.On(handler).Method("AfterStop").WithAnyArguments();
                        NMock2.Expect.Once.On(handler).Method("BeforeUnPause").WithAnyArguments();

                        ExpectProcessedUris(processedUris, handler, directory, engine);
                        RunEngine(engine, waitEvent);
                        CheckErrors(errorCount, webFailureCount);
                    }
                }
            }

        }

        [TestMethod]
        /// <summary>Executes the BeginReadTest_Error test.</summary>
        public void BeginReadTest_Error() {
            TestGateway._ThrowFor = ThrowFor.BeginReadStream;
            TestGateway._ThrowWebException = false;
            PerformWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the BeginReadTest_Web test.</summary>
        public void BeginReadTest_Web() {
            TestGateway._ThrowFor = ThrowFor.BeginReadStream;
            TestGateway._ThrowWebException = true;
            PerformWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the EndReadTest_Error test.</summary>
        public void EndReadTest_Error() {
            TestGateway._ThrowFor = ThrowFor.EndRead;
            TestGateway._ThrowWebException = false;
            PerformWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the EndReadTest_Web test.</summary>
        public void EndReadTest_Web() {
            TestGateway._ThrowFor = ThrowFor.EndRead;
            TestGateway._ThrowWebException = true;
            PerformWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the BeginGetResponse_Error test.</summary>
        public void BeginGetResponse_Error() {
            TestGateway._ThrowFor = ThrowFor.BeginGetResponse;
            TestGateway._ThrowWebException = false;
            PerformWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the BeginGetResponse_Web test.</summary>
        public void BeginGetResponse_Web() {
            TestGateway._ThrowFor = ThrowFor.BeginGetResponse;
            TestGateway._ThrowWebException = true;
            PerformWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the EndGetResponse_Error test.</summary>
        public void EndGetResponse_Error() {
            TestGateway._ThrowFor = ThrowFor.EndGetResponse;
            TestGateway._ThrowWebException = false;
            PerformWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the EndGetResponse_Error test.</summary>
        public void EndGetResponse_Web() {
            TestGateway._ThrowFor = ThrowFor.EndGetResponse;
            TestGateway._ThrowWebException = true;
            PerformWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the GetResponseStream_Error test.</summary>
        public void GetResponseStream_Error() {
            TestGateway._ThrowFor = ThrowFor.GetResponseStream;
            TestGateway._ThrowWebException = false;
            PerformWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the GetResponseStream_Web test.</summary>
        public void GetResponseStream_Web() {
            TestGateway._ThrowFor = ThrowFor.GetResponseStream;
            TestGateway._ThrowWebException = true;
            PerformWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the PauseBeginReadTest_Error test.</summary>
        public void PauseBeginReadTest_Error() {
            TestGateway._ThrowFor = ThrowFor.BeginReadStream;
            TestGateway._ThrowWebException = false;
            PerformPuaseWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the PauseBeginReadTest_Web test.</summary>
        public void PauseBeginReadTest_Web() {
            TestGateway._ThrowFor = ThrowFor.BeginReadStream;
            TestGateway._ThrowWebException = true;
            PerformPuaseWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the PauseEndReadTest_Error test.</summary>
        public void PauseEndReadTest_Error() {
            TestGateway._ThrowFor = ThrowFor.EndRead;
            TestGateway._ThrowWebException = false;
            PerformPuaseWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the PauseEndReadTest_Web test.</summary>
        public void PauseEndReadTest_Web() {
            TestGateway._ThrowFor = ThrowFor.EndRead;
            TestGateway._ThrowWebException = true;
            PerformPuaseWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the PauseBeginGetResponse_Error test.</summary>
        public void PauseBeginGetResponse_Error() {
            TestGateway._ThrowFor = ThrowFor.BeginGetResponse;
            TestGateway._ThrowWebException = false;
            PerformPuaseWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the PauseBeginGetResponse_Web test.</summary>
        public void PauseBeginGetResponse_Web() {
            TestGateway._ThrowFor = ThrowFor.BeginGetResponse;
            TestGateway._ThrowWebException = true;
            PerformPuaseWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the PauseEndGetResponse_Error test.</summary>
        public void PauseEndGetResponse_Error() {
            TestGateway._ThrowFor = ThrowFor.EndGetResponse;
            TestGateway._ThrowWebException = false;
            PerformPuaseWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the PauseEndGetResponse_Error test.</summary>
        public void PauseEndGetResponse_Web() {
            TestGateway._ThrowFor = ThrowFor.EndGetResponse;
            TestGateway._ThrowWebException = true;
            PerformPuaseWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the PauseGetResponseStream_Error test.</summary>
        public void PauseGetResponseStream_Error() {
            TestGateway._ThrowFor = ThrowFor.GetResponseStream;
            TestGateway._ThrowWebException = false;
            PerformPuaseWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the PauseGetResponseStream_Web test.</summary>
        public void PauseGetResponseStream_Web() {
            TestGateway._ThrowFor = ThrowFor.GetResponseStream;
            TestGateway._ThrowWebException = true;
            PerformPuaseWebFailureTest();
        }

        [TestMethod]
        /// <summary>Executes the IsRedirect_Error test.</summary>
        public void IsRedirect_Error() {
            TestGateway._ThrowFor = ThrowFor.IsRedirect;
            TestGateway._ThrowWebException = false;
            PerformErrorTest();
        }

        [TestMethod]
        /// <summary>Executes the IsRedirect_Web test.</summary>
        public void IsRedirect_Web() {
            TestGateway._ThrowFor = ThrowFor.IsRedirect;
            TestGateway._ThrowWebException = true;
            PerformErrorTest();
        }

        [TestMethod]
        /// <summary>Executes the PauseIsRedirect_Error test.</summary>
        public void PauseIsRedirect_Error() {
            TestGateway._ThrowFor = ThrowFor.IsRedirect;
            TestGateway._ThrowWebException = false;
            PerformPauseErrorTest();
        }

        [TestMethod]
        /// <summary>Executes the PauseIsRedirect_Web test.</summary>
        public void PauseIsRedirect_Web() {
            TestGateway._ThrowFor = ThrowFor.IsRedirect;
            TestGateway._ThrowWebException = true;
            PerformPauseErrorTest();
        }

        static void PerformErrorTest() {
            int errorCount = 1;
            int webFailureCount = 0;

            PerformTest(false, errorCount, webFailureCount);
        }

        static void PerformWebFailureTest() {
            int errorCount = 0;
            int webFailureCount = 1;

            PerformTest(false, errorCount, webFailureCount);
        }

        static void PerformPauseErrorTest() {
            int errorCount = 1;
            int webFailureCount = 0;

            PerformTest(true, errorCount, webFailureCount);
        }

        static void PerformPuaseWebFailureTest() {
            int errorCount = 0;
            int webFailureCount = 1;

            PerformTest(true, errorCount, webFailureCount);
        }

        static void PerformTest(bool shouldPause, int errorCount, int webFailureCount) {
            PerformTest(shouldPause, 0, 0, -1, TestGateway._ThrowFor == ThrowFor.None || shouldPause, errorCount, webFailureCount);
        }

        static void PerformTest(bool shouldPause, int readSleepTIme, int getResponseSleepTime, int timeout, bool shouldSucceed, int errorCount, int webFailureCount) {
            int workerCount = 1;
            string[] startingUris = new string[] { "nolinks.html" };
            string[] historyFiles = new string[] { };
            string[] processedUris = new string[] { };
            if (shouldSucceed)
                processedUris = new string[] { "nolinks.html" };

            using (NMock2.Mockery mock = new NMock2.Mockery()) {
                TestEventHandler handler = mock.NewMock<TestEventHandler>();
                using (Eas.WebCrawler.Common.Test.TestDirectory directory = new Eas.WebCrawler.Common.Test.TestDirectory("SimpleScenarioTests", "Resources")) {
                    LoadHistoryFiles(historyFiles, directory);
                    string[] initialUriList = CreateUriList(startingUris, directory);
                    TestGateway gateway = new TestGateway();
                    gateway._GetResponseSleepTime = getResponseSleepTime;
                    gateway._ReadSleepTime = readSleepTIme;
                    using (Eas.WebCrawler.Interfaces.WebCrawlerEngine engine = TestHelper.CreateEngine(gateway, handler, directory, workerCount, timeout, initialUriList)) {
                        Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent waitEvent = PrepareEngine(shouldPause, handler, engine);
                        ExpectProcessedUris(processedUris, handler, directory, engine);
                        RunEngine(engine, waitEvent);
                        CheckErrors(errorCount, webFailureCount);
                    }
                }
            }
        }

        private static void CheckErrors(int errorCount, int webFailureCount) {
            Assert.AreEqual<int>(errorCount, LogErrorAction.errors.Count);
            Assert.AreEqual<int>(webFailureCount, LogWebFailureAction.failures.Count);
        }

        private static void RunEngine(Eas.WebCrawler.Interfaces.WebCrawlerEngine engine, Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent waitEvent) {
            try {
                engine.Start();
                waitEvent.WaitOne();
            }
            catch (System.Exception ex) {
                Console.WriteLine(ex.ToString());
            }
        }

        private static void ExpectProcessedUris(string[] processedUris, TestEventHandler handler, Eas.WebCrawler.Common.Test.TestDirectory directory, Eas.WebCrawler.Interfaces.WebCrawlerEngine engine) {
            UriProcessedCheckAction action = new UriProcessedCheckAction(_ExpectedUris);
            foreach (string uri in CreateUriList(processedUris, directory)) {
                _ExpectedUris.Add(new Uri(uri), null);
                NMock2.Expect.Once.On(handler).Method("UrlProcessed").WithAnyArguments().Will(action);
            }
            Assert.AreEqual<int>(processedUris.Length, _ExpectedUris.Count);
        }

        private static Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent PrepareEngine(bool shouldPause, TestEventHandler handler, Eas.WebCrawler.Interfaces.WebCrawlerEngine engine) {
            Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent waitEvent = new Eas.WebCrawler.Common.Threading.EventHandlerCompletionEvent();
            engine.Events.Stopped += waitEvent.CreateEventHandler();
            engine.Events.TraceLevel = TraceLevel.Detailed;
            LogErrorAction.errors.Clear();
            PauseAndUnPauseAction pauseAction = new PauseAndUnPauseAction(engine, shouldPause);
            NMock2.Expect.AtLeast(0).On(handler).Method("Error").WithAnyArguments().Will(new LogErrorAction());
            NMock2.Expect.AtLeast(0).On(handler).Method("Trace").WithAnyArguments().Will(new LogTraceAction(), pauseAction);
            NMock2.Expect.AtLeast(0).On(handler).Method("UrlFailed").WithAnyArguments().Will(new LogWebFailureAction());
            NMock2.Expect.AtLeast(0).On(handler).Method("UrlProcessingBegan").WithAnyArguments();
            NMock2.Expect.Once.On(handler).Method("BeforeStart").WithAnyArguments();
            NMock2.Expect.Once.On(handler).Method("AfterStop").WithAnyArguments();
            if (shouldPause) {
                NMock2.Expect.Once.On(handler).Method("BeforeUnPause").WithAnyArguments();
                NMock2.Expect.Once.On(handler).Method("AfterPause").WithAnyArguments();
            }
            return waitEvent;
        }

        private static void LoadHistoryFiles(string[] historyFiles, Eas.WebCrawler.Common.Test.TestDirectory directory) {
            if (historyFiles != null) {
                foreach (string file in historyFiles) {
                    TestHelper.AddToHistoryFile(directory, file);
                }
            }
        }

        private static string[] CreateUriList(string[] startingUris, Eas.WebCrawler.Common.Test.TestDirectory directory) {
            string[] initialUriList = new string[startingUris.Length];

            for (int i = 0; i < startingUris.Length; i++) {
                initialUriList[i] = "file://" + directory.CreateFileName(startingUris[i]);
            }
            return initialUriList;
        }
    }
}
